<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>面试题</title>
</head>
<body>
    <script>

        /**
         * 变量以及变量表达式(包括函数的表达式声明)在预编译阶段都只是声明提升，而没有赋值，值都为undefined。
           而声明式函数在预编译阶段将会整体提升,预解析完成之后，代码开始运行，会从上向下一行一行的执行
           但是 function定义函数的位置不会执行，要等到函数被调用的时候再执行
           也就是说：
                var a=123;
                var foo1 =function(){}
                function foo2(){}
            都会在预解析期间被全局的变量对象global收集。但是不同的是a和foo1采用变量表达式只会赋值为undefined
            要在预解析阶段结束之后代码从上向下执行的过程中遇到赋值语句才会真实的赋值
            而foo2使用的是声明式定义在预编译的时候就会被赋值

        */

       

        /**
         *  函数声明置顶；
            变量声明置顶；
            遇到同名时，变量声明比函数声明更优先(置顶)也就是说同名的函数会把同名的变量覆盖掉；
            变量不会重复声明；(重复声明只是会覆盖)
            直接变量声明时赋值，在预编译解析时，会拆成声明和赋值2个部分，声明会置顶，而赋值将保留在原来的位置；
            声明的提升只会提升到当前作用域下的顶层，而不会提升到作用域之外。
            预编译过程中形成的作用域链的查找顺序是由里向外，JS执行顺序是自上往下的。
            **当函数被声明在判断语句内部时，JS会将函数声明当成表达式(赋值声明)即不会提升
        */


           //面试题1:

            /**
             * 在预解析期间变量a和函数a都会类型提升
             * 但是遇到同名时，变量声明比函数声明更优先(置顶)预解析期间变量只会赋值为undefined
             * 函数会被赋值(但只是赋值里面的代码要等到函数调用的时候再执行) 所以a会被赋值为function类型
             */
          
            // console.log(typeof a);  //function
            //     function a(){
            //     }
            //     var a;

           

            /**
             * 看一段JS代码的执行流程要先看预解析期间干了哪些事情(变量提升 赋默认值)
             * 然后执行期间干了哪些事情
            */

            //面试题2:
            /*
               var a = 10;
               function a(){
                  console.log(a);
             }
                a();
                
                /**
                 * 预解析过程中a首先被赋值为undefined 然后被赋值为function
                 * 代码执行过程中 a赋值为10 然后调用a() 此时a是一个变量所以会报is not a function
                */

            //面试题3:
            
            /**
             * b函数调用的时候首先进行函数内的预解析
             * b函数内定义了a函数，预解析的时候a会被赋值为function
             * 
             * 预解析完成之后调用执行函数内的代码：在预解析的时候a是函数
             * 当a=10的时候a的类型变成了10,注意这里的a是函数内的a不会影响全局作用域中的a
             *   console.log(a)输出的是全局中的a
            // */
            //           var a = 1;
            //           function b(){
            //             a = 10;
            //             return;
            //             function a(){
            //                 console.log(a);

            //             }
            //       }
            //          b();
            //          console.log(a)//1

                    //  https://blog.csdn.net/qq_31947477/article/details/105745014
            
                    /**
                     * 函数的内部再定义函数当外部函数调用的时候函数的内部会先进行预解析
                     * 预解析的时候逻辑和代码执行的逻辑不同，不会逐行执行，而是将var 和 function进行变量提升
                     * 函数内部的预解析和全局预解析类似：将var function的类型提升
                    */


                    //面试题4

                     /**
                         * 当函数被声明在判断语句内部时， JS会将函数声明当成表达式(赋值声明)即不会提升
                     */

                    // var  a = 0;
                    // if(true){
                    //     a = 1;  //作用域中不存在a变量，因此会通过作用域链向外查找，找到全局中的a变量，并将1赋值
                    //     function a(){}  //这是函数声明，相当于定义了一个函数a，并给其分配了内存
                    //     a = 21;  //此时作用域中已经存在了a变量(a函数)，因此该赋值将直接赋值给作用域中的a而不是全局的a
                    //     console.log('里面', a);  //21，由于作用域中存在a变量了，因此直接打印作用域中的a
                    // }
                    // console.log('外面', a);   //1，全局作用域中存在a变量，并赋值为1，因此打印1

                    /**
                     * 函数在预解析的时候的顺序和执行的时候不一样
                    */

                //             var a = 1;
                //           function b(){
                //             a = 10;
                //             return;
                //             function a(){
                //                 console.log(a);

                //             }
                //   }
                //        b();

                /*
                   调用b函数预解析：
                   a=function;

                   

                
                */
                    
    </script>
</body>
</html>